{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### GW灰度世界白平衡算法\n",
    "\n",
    "灰度世界算法（Gray World)是以灰度世界假设为基础的,该假设认为对于一幅有着大量色彩变化的图像, R、 G、 B 三个分量的平均值趋于同一个灰度K。\n",
    "$$Rgain = k / Ravg $$\n",
    "$$Ggain = k / Gavg $$\n",
    "$$Bgain = k / Bavg $$\n",
    "然后通过k值来求各个通达的增益。\n",
    "1. 直接给定为固定值, 取其各通道最大值的一半,即取为127或128；\n",
    "2. 令 K = (Raver+Gaver+Baver)/3,其中Raver,Gaver,Baver分别表示红、 绿、 蓝三个通道的平均值。\n",
    "算法的第二步是分别计算各通道的增益：\n",
    "$$k =  (Bavg + Gavg + Ravg)/3$$\n",
    "3. 设定G通道不变，让R和B通道往G通道上靠，即：\n",
    "$$Rgain = Rave / Gave$$\n",
    "$$Bgain = Bave / Gave$$\n",
    "新的像素的绿色通道不变，红色和蓝色通道通过绿色通道来求"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# _*_ coding: UTF-8 _*_\n",
    "# path:\n",
    "\n",
    "\"\"\"\n",
    "作者：朱文涛\n",
    "邮箱：wtzhu_13@163.com\n",
    "\n",
    "时间：2019/05\n",
    "描述：灰度世界法实现白平衡\n",
    "\"\"\"\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "\n",
    "src = cv.imread('test.jpg')\n",
    "\n",
    "# 求出各个颜色分量的平均值\n",
    "b_avg = np.mean(src[:, :, 0])\n",
    "g_avg = np.mean(src[:, :, 1])\n",
    "r_avg = np.mean(src[:, :, 2])\n",
    "# 求出灰度世界的灰度值\n",
    "k = (b_avg + g_avg + r_avg)/3\n",
    "# 求出各个颜色分量的增益\n",
    "b_gain = k / b_avg\n",
    "g_gain = k / g_avg\n",
    "r_gain = k / r_avg\n",
    "\n",
    "# 定义一个新的矩阵存放变换后的图像\n",
    "src1 = np.zeros(src.shape)\n",
    "src1[:, :, 0] = src[:, :, 0] * b_gain\n",
    "src1[:, :, 1] = src[:, :, 1] * g_gain\n",
    "src1[:, :, 2] = src[:, :, 2] * r_gain\n",
    "\n",
    "# 计算后类型为浮点数，需要类型转换\n",
    "src1 = src1.astype(np.uint8)\n",
    "\n",
    "# 拼接两张图片，便于观察\n",
    "img = np.hstack([src, src1])\n",
    "cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE)\n",
    "cv.imshow('input_image', img)\n",
    "cv.waitKey(0)\n",
    "cv.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PR完全反射白平衡算法\n",
    "图像中最亮的点为白点，那么各个通道的最大值应该趋近白色，可是设定一个K值，如255,那么图像中最两点的值应该趋于255，这样就可以计算出各通道的增益：\n",
    "$$Rgain = k / Rmax $$\n",
    "$$Ggain = k / Gmax $$\n",
    "$$Bgain = k / Bmax $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# _*_ coding: UTF-8 _*_\n",
    "# path:\n",
    "\n",
    "\"\"\"\n",
    "作者：朱文涛\n",
    "邮箱：wtzhu_13@163.com\n",
    "\n",
    "时间：2019/05\n",
    "描述：全反射实现白平衡\n",
    "\"\"\"\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "\n",
    "src = cv.imread('test.jpg')\n",
    "\n",
    "# 求出各个颜色分量的增益\n",
    "b_gain = 255 / np.max(src[:, :, 0])\n",
    "g_gain = 255 / np.max(src[:, :, 1])\n",
    "r_gain = 255 / np.max(src[:, :, 2])\n",
    "\n",
    "# 定义一个新的矩阵存放变换后的图像\n",
    "src1 = np.zeros(src.shape)\n",
    "src1[:, :, 0] = src[:, :, 0] * b_gain\n",
    "src1[:, :, 1] = src[:, :, 1] * g_gain\n",
    "src1[:, :, 2] = src[:, :, 2] * r_gain\n",
    "\n",
    "# 计算后类型为浮点数，需要类型转换\n",
    "src1 = src1.astype(np.uint8)\n",
    "\n",
    "# 拼接两张图片，便于观察\n",
    "img = np.hstack([src, src1])\n",
    "cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE)\n",
    "cv.imshow('input_image', img)\n",
    "cv.waitKey(0)\n",
    "cv.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### QCGP白平衡算法\n",
    "\n",
    "通过GW和PR两种算法的正交组合，从而保留两者的优点，具体的算法公式如下：\n",
    "$$ Kave = (Rave + Gave + Bave) / 3 $$\n",
    "$$ Kmax = (Rmax + Gmax + Bmax) / 3$$\n",
    "$$ u * Rave^2 + v * Rave = Kave $$\n",
    "$$ u * Rmax^2 + v* Rmax = Kmax $$\n",
    "这是R通道的算法公式，求出u和v然后通过以下公式换算出新的值：\n",
    "$$Rnew = u * Rorg ^ 2 + v * Rorg$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# _*_ coding: UTF-8 _*_\n",
    "# path:\n",
    "\n",
    "\"\"\"\n",
    "作者：朱文涛\n",
    "邮箱：wtzhu_13@163.com\n",
    "\n",
    "时间：2020/04\n",
    "描述：GW和PR正交组合算法白平衡QCGP\n",
    "\"\"\"\n",
    "\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "\n",
    "src0 = cv.imread('..//images//NikonD5200_0001_G_AS.png')\n",
    "src = src0.astype(np.uint16)    # 调整一下数据类型，防止算术运算溢出\n",
    "\n",
    "# 求出各个颜色分量的平均值\n",
    "b_ave = np.mean(src[:, :, 0])\n",
    "g_ave = np.mean(src[:, :, 1])\n",
    "r_ave = np.mean(src[:, :, 2])\n",
    "\n",
    "# 各个颜色分量的最大值\n",
    "b_max = np.max(src[:, :, 0])\n",
    "g_max = np.max(src[:, :, 1])\n",
    "r_max = np.max(src[:, :, 2])\n",
    "\n",
    "# 根据QCGP公式求出系数\n",
    "k_ave = (b_ave + g_ave + r_ave)/3\n",
    "k_max = (b_max + g_max + r_max)/3\n",
    "k_matrix = np.mat([[k_ave], [k_max]])\n",
    "\n",
    "# 通过矩阵求出B通道的转换矩阵，并计算出新图的B通道\n",
    "b_coefficient_matrix = np.mat([[b_ave * b_ave, b_ave],\n",
    "                               [b_max * b_max, b_max]])\n",
    "b_conversion_matrix = b_coefficient_matrix.I * k_matrix\n",
    "\n",
    "b = (src[:, :, 0]).transpose()\n",
    "bb = (src[:, :, 0] * src[:, :, 0]).transpose()\n",
    "b = np.stack((bb, b), axis=0).transpose()\n",
    "b_des = np.dot(b, np.array(b_conversion_matrix))\n",
    "b_des = b_des.astype(np.uint8).reshape([280, 471])\n",
    "\n",
    "# 通过矩阵求出G通道的转换矩阵，并计算出新图的G通道\n",
    "g_coefficient_matrix = np.mat([[g_ave * g_ave, g_ave],\n",
    "                               [g_max * g_max, g_max]])\n",
    "g_conversion_matrix = g_coefficient_matrix.I * k_matrix\n",
    "\n",
    "g = (src[:, :, 1]).transpose()\n",
    "gg = (src[:, :, 1] * src[:, :, 1]).transpose()\n",
    "g = np.stack((gg, g), axis=0).transpose()\n",
    "g_des = np.dot(g, np.array(g_conversion_matrix))\n",
    "g_des = g_des.astype(np.uint8).reshape([280, 471])\n",
    "\n",
    "# 通过矩阵求出R通道的转换矩阵，并计算出新图的R通道\n",
    "r_coefficient_matrix = np.mat([[r_ave * r_ave, r_ave],\n",
    "                               [r_max * r_max, r_max]])\n",
    "r_conversion_matrix = r_coefficient_matrix.I * k_matrix\n",
    "\n",
    "r = (src[:, :, 2]).transpose()\n",
    "rr = (src[:, :, 2] * src[:, :, 2]).transpose()\n",
    "r = np.stack((rr, r), axis=0).transpose()\n",
    "r_des = np.dot(r, np.array(r_conversion_matrix))\n",
    "r_des = r_des.astype(np.uint8).reshape([280, 471])\n",
    "\n",
    "# 用一个新的矩阵接受新的图片，注意数据类型要和原图一致\n",
    "src1 = np.zeros(src.shape).astype(np.uint8)\n",
    "src1[:, :, 0] = b_des\n",
    "src1[:, :, 1] = g_des\n",
    "src1[:, :, 2] = r_des\n",
    "\n",
    "# 显示图片\n",
    "img = np.hstack([src0, src1])\n",
    "cv.namedWindow(\"AWB\", cv.WINDOW_AUTOSIZE)\n",
    "cv.imshow(\"AWB\", img)\n",
    "cv.waitKey(0)\n",
    "cv.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
